#include "CJSONTextEdit.h"
#include <QDebug>
#include <QMimeData>
#include <QMap>

#include "components/CommonFunc.h"
#include "components/GlobalComponents.h"

#define  MAX_SPACING_NUM       (20)             // 空格字符值
static QMap<int, QString>      gmapSpaceString; // 静态，预留空格配置

CJSONTextEdit::CJSONTextEdit(QWidget *parent)
              : QTextEdit(parent)
              , m_bParseText(false)
{
    m_bTextChanged = false;
    m_strTextSrc.clear();
    connect(this, SIGNAL(textChanged()), this, SLOT(slt_textChanged()));   // 监测文本变化，定时更新行数

    // 分配预留空白内容
    if(gmapSpaceString.isEmpty())
    {
        QString spacing = "";
        gmapSpaceString[0] = spacing;
        for (int idx = 1; idx <= MAX_SPACING_NUM; idx++)
        {
            spacing.append("    ");
            gmapSpaceString[idx] = spacing;
        }
    }
}

void CJSONTextEdit::slt_textChanged()
{
    //qDebug() << "slt_textChanged" << m_bTextChanged;
    m_bTextChanged = true;
}

// 监测数据插入
void CJSONTextEdit::insertFromMimeData(const QMimeData *source)
{
    // 确认是否存在全选，如果选中全部数据，则清空原来的，再插入新数据并解析
    QString strSelectPre = textCursor().selectedText();
    if(!strSelectPre.isEmpty())
    {
        if(m_strTextSrc != strSelectPre.toStdString())
        {
            clear();
        }
    }

    if (source->hasText())
    {
        if(!document()->toPlainText().isEmpty())
        {
            // 当前文本中不为空，且待粘贴内容不为空，直接在光标位置插入
            textCursor().insertText(source->text().trimmed());
            return;
        }

        // 文本框为空，则将内容解析后插入
        std::string strSrc = source->text().trimmed().toStdString();
        if(false == isFileAndRead(strSrc))
        {
            return;
        }

        if(false == insertJSONText(strSrc))
        {
            append(QString("输入字段无法解析，请重新输入，源报文 :"));
            append(QString::fromStdString(strSrc));
            myMessageBoxShowOne(FORM_NULL, MSG_SHOW_ERROR, QString("输入字段无法解析，请检查输入内容, 当前内容长度值: %1 byte").arg(QString::number(strSrc.length())));
        }
    }
    else
    {
        QTextEdit::insertFromMimeData(source);
    }
}

// 判断是否为文件，对于文件内容则读取文件数据
bool CJSONTextEdit::isFileAndRead(std::string &strSrc)
{
    bool bRet = false;

    do
    {
        if(strSrc.empty())
        {
            qDebug() << "CJSONTextEdit::isFileAndRead strSrc is empty, len: " << strSrc.length();
            break;
        }

        std::string strSub = strSrc.substr(0, 8);
        if(strSub == std::string("file:///"))
        {
            // 确认是属于文件路径
            QString strFile = QString::fromStdString(strSrc.substr(8, strSrc.length() - 8));
            if(false == FileExist(strFile))
            {
                 myMessageBoxShowOne(FORM_NULL, MSG_SHOW_ERROR, QString("指定文件[%1]不存在，请确认!").arg(strFile));
                 break;
            }
            else
            {
                // 指定文件存在，则需获取该文件全部内容，以二进制方式读取整个文件，并将数据通过参数返回
                strSrc.clear();
                if(false == ReadFileByBinary(strFile.toStdString(), strSrc))
                {
                    // 读取失败，发出警告
                    myMessageBoxShowOne(FORM_NULL, MSG_SHOW_ERROR, QString("指定文件[%1]读取数据失败，请确认!").arg(strFile));
                    break;
                }
            }
        }

        bRet = true;

    }while(0);

    return bRet;
}

// 获取原始数据并更新变动状态
bool CJSONTextEdit::getSourceData(std::string &strTextSrc)
{
    bool bTextChanged = m_bTextChanged;
    strTextSrc.clear();
    if(false == m_bTextChanged)
    {
        // 未改变，直接返回原始数据
        strTextSrc = m_strTextSrc;
    }
    else
    {
        strTextSrc = document()->toPlainText().toStdString();
    }

    //qDebug() << "CJSONTextEdit::getSourceData ====" << strTextSrc.size() << m_bTextChanged;

    m_bTextChanged = false;

    return bTextChanged;
}

// 清空数据
void CJSONTextEdit::clearAllData()
{
    this->clear();
    m_bTextChanged = true;
    m_strTextSrc.clear();
}

// 对输入文本进行JSON解析，否则为普通的QTextEdit
void CJSONTextEdit::setParseStatus(const bool bStatus)
{
    m_bParseText = bStatus;
}

// 外部给入JSON文本并更新
void CJSONTextEdit::updateText(std::string &strContext)
{
    if(strContext.empty())
    {
        clearAllData();
    }

    // 数据与原来的不同或存在变动，更新数据
    if(strContext != m_strTextSrc || true == m_bTextChanged)
    {
        //qDebug() << "CJSONTextEdit::updateText m_bTextChanged" << m_bTextChanged;
        insertJSONText(strContext);
    }

    //qDebug() << "CJSONTextEdit::updateText m_bTextChanged" << m_bTextChanged;

    // 修改状态在输入数据时被改动，该状态仅监测输入后被改动的状态
    m_bTextChanged = false;
}

// 插入JSON文本并格式化
bool CJSONTextEdit::insertJSONText(std::string &strJson)
{
    // 先清空所有数据
    this->clear();

    if(false == m_bParseText)
    {
        // 先清空所有数据
        this->append(QString::fromStdString(strJson));
        m_strTextSrc = strJson;
        return true;
    }

    bool bRet = false;
    cJSON *pRoot = nullptr;

    do
    {
        if(strJson.empty() || false == isValidJSON(strJson))
        {
            break;
        }

        pRoot = cJSON_Parse(strJson.c_str());
        if(nullptr != pRoot)
        {
            printJsonInfo(pRoot, 0);
        }
        else
        {
            break;
        }

        m_strTextSrc = strJson;

        bRet = true;

    }while(0);

    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    return bRet;
}

// 将JSON报文逐行打印
void CJSONTextEdit::printJsonInfo(cJSON *pObj, int depth)
{
    if(nullptr == pObj)  return;

    QString strPreSpacing = gmapSpaceString[depth];
    QString strEndingSymbol = (nullptr == pObj->next) ? "" : ",";                               // 确认当前对象是否结束，结束追加逗号
    QString strKey = (nullptr == pObj->string) ? "" : QString("\"%1\" : ").arg(pObj->string);   // 存在key值时，添加key值并追加冒号，否则为空字符
    switch (pObj->type)
    {
    case cJSON_False:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span>false%3").arg(strPreSpacing, strKey, strEndingSymbol));
        break;
    }
    case cJSON_True:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span>true%3").arg(strPreSpacing, strKey, strEndingSymbol));
        break;
    }
    case cJSON_NULL:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span>null%3").arg(strPreSpacing, strKey, strEndingSymbol));
        break;
    }
    case cJSON_Number:
    {
        QString strNum = QString::number(pObj->valueint);
        if((pObj->valuedouble - pObj->valueint) > 0.000001)
        {
            strNum = QString::number(pObj->valuedouble, 'f', 6);
        }

        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span><span style='color:#128BF1;'>%3</span>%4").arg(strPreSpacing, strKey, strNum, strEndingSymbol));
        break;
    }
    case cJSON_String:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span><span style='color:#54B54A;'>\"%3\"</span>%4").arg(strPreSpacing, strKey, pObj->valuestring, strEndingSymbol));
        break;
    }
    case cJSON_Array:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span>[").arg(strPreSpacing, strKey));
        printJsonInfo(pObj->child, depth + 1);
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1</span>]%2").arg(strPreSpacing, strEndingSymbol));
        break;
    }
    case cJSON_Object:
    {
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1%2</span>{").arg(strPreSpacing, strKey));
        printJsonInfo(pObj->child, depth + 1);
        append(QString("<span style='white-space:pre;color:#9C278F;'>%1</span>}%2").arg(strPreSpacing, strEndingSymbol));
        break;
    }
    default:
        break;
    }

    printJsonInfo(pObj->next, depth);
}